#include "Ogre.h" 
// Template on this class to get the template requirement out of the way 
// in cases where you don't want to use member function callbacks. 
class _Empty 
  {}; 

// template this class on the class containing your timer callbacks .. 
// that is, if you want to use member function callbacks. 
template <typename callbackClass > 
class TimerManager : public Ogre::FrameListener 
  { 
  public: 
    // m_use_frame_started true will cause timer callbacks to be sent 
    // during frame started.  False: callbacks will be sent 
    // during frame ended. 
    TimerManager( bool _m_use_frame_started = true ): m_use_frame_started( _m_use_frame_started ) 
      {} 

    // Two choices of callbacks. 
    // You can template this class on your class 
    // and use it to call member functions 
    // if your handler returns false, the timer will NOT be m_repeated. 
    typedef bool (callbackClass::*MemberTimerHandler)( int ); 
    // or you can provide a pointer to a static or global function 
    typedef bool (*TimerCallback)( int m_timer_id ); 

    // AddTimer() adds a timer to be called back after a given m_period 
    // m_timer_id is an optional identifier for later removal 
    // m_repeat is a boolean flag, if true the timer handler will be called 
    // every m_period untill stopped.  if false, timer handler will be called only once. 
    // m_play_catchup is a boolean flag, if true the timer handler will be called multiple times 
    // if TimerManager is unable to call at the requested rate due to low frame rate.    
    // the only difference between these two add functions is one uses member function callback 
    // the other uses a global callback. 
    void AddTimer( unsigned long m_period, TimerCallback callback, int m_timer_id = -1, bool m_repeat = false, 
      bool m_play_catchup = true ) 
      { 
      TimerInstance t(NULL, NULL, callback, m_period, m_timer_id, m_repeat,  m_play_catchup);
      unsigned long now = m_timer.getMilliseconds(); 
      m_timers.insert( std::make_pair(now + m_period, t) ); 
      } 
    void AddTimer( unsigned long m_period, MemberTimerHandler callback, callbackClass* callbackObject, 
      int m_timer_id = -1, bool m_repeat = false, bool m_play_catchup = true ) 
      { 
      TimerInstance t(callback, callbackObject, NULL, m_period, m_timer_id, m_repeat, m_play_catchup);
      unsigned long now = m_timer.getMilliseconds(); 
      m_timers.insert( std::make_pair(now + m_period, t) ); 
      } 

    // searches for and removes all m_timers with given m_timer_id 
    // note: If all the m_timers have the same ID, and that ID is prided here, it will end ALL m_timers 
    void StopTimer( int m_timer_id ) 
      { 
      typename TimerMap::iterator i; 
      for( i = m_timers.begin(); i != m_timers.end(); ++i ) 
        { 
        if( i->second.m_timer_id == m_timer_id ) 
          { 
          m_timers.erase(i); 
          // we have to start our search over since erase invalidates our iterator 
          i = m_timers.begin(); 
          } 
        } 
      } 
  protected: 
    // Ogre::Timer to watch time pass by. 
    Ogre::Timer m_timer; 
    // definition of our timer instance. 
    class TimerInstance 
      { 
      public:
        TimerInstance(MemberTimerHandler i_member_time_handler, callbackClass* ip_callback_class, TimerCallback i_callback, 
          unsigned long i_period, int i_timer_id, bool i_repeat, bool i_play_catchup) :
        m_member_time_handler(i_member_time_handler), 
        mp_callback_class(ip_callback_class), 
        m_callback(i_callback), 
        m_period(i_period), 
        m_timer_id(i_timer_id), 
        m_repeat(i_repeat), 
        m_play_catchup(i_play_catchup) {}
        unsigned long Trigger(unsigned long behind) 
          { 
          bool callback_return = false; 
          long temp_behind = (long)behind; 
          while( temp_behind >= 0 ) 
            { 
            temp_behind -= m_period; 
            callback_return = Callback(); 
            if( !m_repeat ) 
              return 0; 
            if( !callback_return ) 
              return 0; 
            if( m_period == 0 ) 
              return 0; 
            if( !m_play_catchup ) 
              continue; 
            } 

          return (m_period - (behind % m_period)); 
          } 
        bool Callback() 
          { 
          if( ( m_member_time_handler != NULL ) && (mp_callback_class != NULL ) ) 
            { 
            return (mp_callback_class->*m_member_time_handler)(m_timer_id); 
            } 
          else if ( m_callback != NULL ) 
            { 
            return m_callback(m_timer_id); 
            } 
          return false; 
          }
      private:
        MemberTimerHandler m_member_time_handler; 
        callbackClass* mp_callback_class; 
        TimerCallback    m_callback; 
        unsigned long m_period; 
        int m_timer_id; 
        bool m_repeat; 
        bool m_play_catchup; 
      }; 
    // inherited frameStarted and frame Ended from Ogre::FrameListener 
    virtual bool frameStarted(const Ogre::FrameEvent& evt) 
      { 
      if( !m_use_frame_started ) 
        return true; 
      while(Next()); 
      return true; 
      } 
    virtual bool frameEnded(const Ogre::FrameEvent& evt) 
      { 
      if( m_use_frame_started ) 
        return true; 
      while(Next()); 
      return true; 
      } 
    // process next timer (if applicable) 
    bool Next() 
      { 
      if ( m_timers.empty() ) 
        return false; 
      unsigned long now = m_timer.getMilliseconds(); 
      unsigned long then = m_timers.begin()->first; 
      //std::cout << " now >= then " << now << " >= " << then << std::endl; 
      if(now >= then) 
        { 
        TimerInstance current_timer = m_timers.begin()->second; // get a copy of the timer instance 
        m_timers.erase(m_timers.begin()); // pop the sucker off the map. 
        unsigned long next_trigger = current_timer.Trigger( now - then ); // trigger it 
        if( next_trigger > 0 ) 
          { 
          // if it returns non-zero, put it back in the map. 
          m_timers.insert(std::make_pair(now + next_trigger, current_timer)); 
          } 
        return true; 
        } 
      return false; 
      } 
    // container for all our timer instances. 
    typedef std::multimap< int, TimerInstance > TimerMap; 
    TimerMap m_timers; 
    // determines if callbacks happen durring frame started, or frame ended. 
    bool m_use_frame_started; 
  };